#include "HerzeleidPrecompiled.hxx"
#include "HerzeleidCoreApplication.hxx"
#include "HerzeleidCoreApplicationView.hxx"
#include "HerzeleidCoreWindow.hxx"
#include "HerzeleidArray.hxx"

using namespace HerzeleidUICoreNamespaceImpl;
using namespace HerzeleidAppModelCoreNamespaceImpl;
#define TaskCancellationRequested concurrency::is_task_cancellation_requested
#define CancelCurrentTask concurrency::cancel_current_task

// Static events and members

//CoreApplication::AsyncCancellationTokenSource *CoreApplication::s_MessageLoopCancellationHandle = nullptr;
CoreApplication::AsyncAction *CoreApplication::s_MessageLoopHandle = nullptr;
CoreApplicationViewCollection CoreApplication::s_vApplicationViews;
CoreApplication *CoreApplication::s_pApplicationInstance = nullptr;
//CoreApplication::SuspendingEvent CoreApplication::Suspending;
//CoreApplication::ResumingEvent CoreApplication::Resuming;
//CoreApplication::ExitingEvent CoreApplication::Exiting;

// Constructor

CoreApplication::CoreApplication( InstanceHandleConstRef Instance ) : 
	m_hInst( Instance )
{
	if (!s_pApplicationInstance) 
	{
		s_pApplicationInstance = this;
	}
	else 
	{
		throw std::exception("Herzeleid. Only one app instance is allowed.");
	}
}

CoreApplication::~CoreApplication()
{
	PrintToDebugWindowOnly8("Herzeleid. Destucting core app instance.");
}

CoreApplicationViewCollectionViewPtr CoreApplication::Views()
{
	return( &s_vApplicationViews );
}

CoreApplicationViewPtr CoreApplication::MainView()
{
	for ( auto ViewPtr : s_vApplicationViews ) 
		if ( ViewPtr->IsMain ) 
			return( ViewPtr ); 

	return( s_vApplicationViews[0] );
}

void CoreApplication::Run( _In_ IFrameworkViewSourcePtr pViewSource )
{

	if (s_vApplicationViews.size()) return; // Kinda access guard :)

	PrintToDebugWindowOnly8("Herzeleid. Creating window.");
	auto lWindow = new CoreWindow;
	lWindow->m_ModuleHandle = s_pApplicationInstance->m_hInst;
	lWindow->m_bIsFullscreen = false;
	lWindow->m_BoundingRect.X = 50.0f;
	lWindow->m_BoundingRect.Y = 50.0f;
	lWindow->m_BoundingRect.Width = 800.0f;
	lWindow->m_BoundingRect.Height = 600.0f;

	PrintToDebugWindowOnly8("Herzeleid. Registering window.");
	lWindow->RegisterWindow();

	PrintToDebugWindowOnly8("Herzeleid. Creating application view.");
	s_vApplicationViews.push_back(
		new CoreApplicationView()
		);

	// Create  window with the splash screen and run it
	// without any event dispatching on a other thread. 

	// Create and configure currect main application view. 
	auto pAppView = s_vApplicationViews.front(); // Get the first element added. 
	pAppView->m_bIsHosted = false; // Is not hosted (TODO: Figure out why).
	pAppView->m_bIsMain = true; // Make it main. 

	// Create framework view using the provided framework view source (hopefully) implementation. 
	PrintToDebugWindowOnly8("Herzeleid. Creating framework view.");
	auto pView = pViewSource->CreateView();

	// Let user to configure app and current app view events. 
	PrintToDebugWindowOnly8("Herzeleid. Initializing framework view.");
	pView->Initialize( pAppView );

	// Let user to upload all the required resources to start the app view. 
	PrintToDebugWindowOnly8("Herzeleid. Loading external framework view resources.");
	pView->Load( L"HerzeleidDemo.App" );

	// Clear splash window. 
	// Assign dispatcher for the splash window. 
	// Let the user to configure the window events. 
	PrintToDebugWindowOnly8("Herzeleid. Associating window with the framework view.");
	pAppView->m_AssociatedWindow = lWindow;
	pView->SetWindow( pAppView->CoreWindow );

	// Create main message loop. 
	//s_MessageLoopCancellationHandle = new AsyncCancellationTokenSource();
	PrintToDebugWindowOnly8("Herzeleid. Launching application message loop.");
	s_MessageLoopHandle = new AsyncAction( [pView]( Void ) 
	{ 
		pView->Run();
		return( true );
	});
	//}, s_MessageLoopCancellationHandle->get_token());

	MSG msg;
	bool bLoop = true;

	while (bLoop)
	{
		while ( PeekMessage((&msg), null, 0, 0, PM_REMOVE) )
		{
			if ( msg.message == WM_QUIT )
			{
				bLoop = false;
				// TODO: Quit application
				// Pass quit events to app view, window etc 
				break;
			}

			TranslateMessage( &msg );
			DispatchMessage( &msg );
		}
	} 

	PrintToDebugWindowOnly8("Herzeleid. Process window events.");
	if ( s_MessageLoopHandle )
	{
		AutoDebugVariable( Status ) s_MessageLoopHandle->wait();
	}
	// s_MessageLoopHandle->get();

	// Let the user cleanup all the resources he had loaded previously. 
	PrintToDebugWindowOnly8("Herzeleid. Uninitialize application view.");
	pView->Uninitialize();

	PrintToDebugWindowOnly8("Herzeleid. Deleting framework view.");
	Safe_Delete( pView ); // Release app view
	PrintToDebugWindowOnly8("Herzeleid. Deleting framework view source.");
	Safe_Delete( pViewSource ); // Release app view provider 
}

void CoreApplication::Exit()
{
	// Destroy app message loop. 

	try
	{
		//s_MessageLoopCancellationHandle->cancel();
		PrintToDebugWindowOnly8("Herzeleid. Waiting until the application message loop is over.");
		AutoDebugVariable( Status ) s_MessageLoopHandle->wait();
		//AutoDebugVariable( Result ) s_MessageLoopHandle->get();
	}
	catch ( AsyncActionCanceledErrorConstRef Error )
	{
		// Most likely, we wont reach this point, until the get() function is called. 
		PrintToDebugWindowOnly8( Error.what() );
	}
	catch ( ErrorConstRef Error )
	{
		// The exceptions, that were raised while the task dispatching should appear here. 
		// And we want to know "what()" exactly happend. 
		PrintToDebugWindowOnly8( Error.what() );
	}

	//Safe_Delete( s_MessageLoopCancellationHandle );
	PrintToDebugWindowOnly8("Herzeleid. Deleting application message loop handle.");
	Safe_Delete( s_MessageLoopHandle );

	// Loop through each app view and close them
	PrintToDebugWindowOnly8("Herzeleid. Deleting application views.");
	for ( auto ViewPtr : s_vApplicationViews )
		// Each app view will close the associated window 
		Safe_Delete( ViewPtr ); 

	// Cleanup all the app views
	s_vApplicationViews.clear();

	// ...
	CoUninitialize();
}

#pragma region Application Startup

// Copied from crt\crtw32\h\internal.h to avoid having to include it here.
typedef struct
{
    int newmode;
} _startupinfo;

extern "C" __declspec(dllimport) int __cdecl __wgetmainargs (
	Int32 *pargc,				// Num args
	Char16 ***pargv,			// Args
	Char16 ***penvp,			// Env
	Int32 dowildcard,			// Wildcard expension
	_startupinfo *startinfo		// Malloc usage notifier
	);

Char16** GetCmdArguments( _Out_ int* argc )
{
    assert( argc != nullptr );
    Char16Address envp = nullptr;
    Char16Address argv = nullptr;

    // 'newmode' indicates whether malloc should invoke newhandler on allocation failure
    _startupinfo startinfo = { 0 };

    // No wildcard expansion by default
    int ret = __wgetmainargs(
		argc, &argv, &envp, false, 
		&startinfo
		);

    if (ret < 0) (*argc) = 0;
    return( argv );
}

Int32 main( _In_ SharedStringArray Args );
CoreApplication *CoreApplication::Startup( 
	InstanceHandleConstRef Instance 
	)
{
	if ( s_vApplicationViews.size() ) 
		return (nullptr);

	CoInitialize(nullptr);

	auto iArcCount = 0;
	auto pArgs = GetCmdArguments( _Outptr_ &iArcCount );
	//SharedStringArray SharedArgs( new StringPtrArray(iArcCount) );
	for( auto iArg = 0; iArg < iArcCount; iArg++ ) 
	{
		PrintToDebugWindowOnly16(pArgs[iArg]);

		//Platform::StringArg Arg(new String16(pArgs[iArg]));
		//SharedArgs->Set( iArg, Arg );
	}

	/*for ( auto iArg = 0; iArg < iArcCount; iArg++)
		Safe_Delete(pArgs[iArg]);*/
	//Safe_DeleteArray(pArgs);

	auto App = new CoreApplication( Instance );

	auto Status = main(nullptr); // Useless
	return( App );
}

#pragma endregion